package com.demo.webapp.action;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.views.util.ContextUtil;
//import org.apache.struts2.views.util.UrlHelper;
import org.springframework.context.i18n.LocaleContextHolder;

import com.demo.webapp.config.Constants;
import com.demo.core.model.Site;
import com.demo.webapp.context.MultisiteContextHolder;
import com.demo.webapp.validator.ValidationResolver;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.RuntimeConfiguration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.Validator;

/**
 * 基础Action对象
 * 提供访问HTTP页面的基础方法.
 * 1.提供server验证转换client验证的方法
 * 2.提供callbac功能
 * 3.带有HTTP请求所需要的HttpServletRequest和HttpServletResponse对象
 * 4.提供用户设定的国际化语言、提供当前url地址等参数
 * 
 * @author ryuu.kk
 *
 */
public class BaseActionSupport extends ActionSupport implements ClientValidateable {

	/**
	 * 版本序列化ID
	 */
	private static final long serialVersionUID = 6578624034783758561L;

    /**
     * 'transient' 可以防止session同步问题,子类可以使用该log对象
     */
    protected final transient Log log = LogFactory.getLog(getClass());
    
    /**
     * Action验证管理器
     */
    private ActionValidatorManager actionValidatorManager;
    
    /**
     * Struts配置对象
     */
    private Configuration configuration;
    
    /**
     * 对象工厂
     * 如果是spring管理Struts2对象,该对象为Spring的Ioc容器
     */
    private ObjectFactory objectFactory;
    
    /**
     * ActionMapper
     * Action配置向导内容
     */
    private ActionMapper actionMapper;
    
    /**
     * URL帮助类
     */
    //private UrlHelper urlHelper;
    
    /**
     * 当前验证器列表
     */
    @SuppressWarnings("rawtypes")
	private List<Validator> currentValidators;
    
    private ValidationResolver validationResolver;
    
    /**
     * 取消动作的常量字段
     */
    public static final String CANCEL = "cancel";
    
    /**
     * 回退动作的常量字段
     * 一般执行操作后要重定向到原始页面
     */
    public static final String CALLBACK = "callback";

    /**
     * 该行为是一个ajax操作 
     * 如果是一个ajax请求,装饰器将不做装饰
     */
    protected boolean ajax = false;
    
    /**
     * 该行为为一个页面的一个piece部分
     * 这样方便得到全部页面或页面的一个部分
     * 比如,一个请求,包含有两个piece
     * 一个是主要内容块,一个是辅助内容块
     * 当piece为false时,则主要内容块和辅助内容块同时提供给view层
     * 当piece为ture是,只有主要内容块提供给view层。
     * 这样做方便一些页面分别嵌入页面本身的导航(或功能块,但并不是一个可以统一放入装饰器进行装饰的内容)
     * 和分页table(table分页又采用了ajax技术)。
     * 注意,该字段与ajax字段有着本质的区别.
     * ajax字段为url重写和装饰器等提供处理依据
     * piece字段为control层和view层提供处理依据
     */
    protected boolean piece = false;
    
    /**
     * 当出现值栈表达式出现错误时,是否抛出异常
     */
    protected boolean throwExceptionOnELFailure = false;
    
    /**
     * 请求页
     * 记录当前访问的地址是由哪个地址跳转而来
     */
    private String referer;
    
    /**
     * 回退地址
     * 当url地址没有指定回退url时,使用referer
     */
    private String backurl;
    
    /**
     * 返回"cancel"
     * 提供Action的Results结果
     *
     * @return "cancel"
     */
    public String cancel() {
        return CANCEL;
    }
    
    /**
     * URL参数的utf-8编码
     * @param url URL地址
     * @return encode url
     */
    public String urlEncode(String url) {
    	String backurl = "";
    	try {
			backurl = URLEncoder.encode(url, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    	return backurl;
    }
    
    /**
     * URL参数的utf-8解码
     * @param url URL地址
     * @return decode url
     */
    public String urlDecode(String url) {
    	String backurl = "";
    	try {
    		backurl = URLDecoder.decode(url,"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    	return backurl;
    }
    
    /**
     * 返回当前页面的引用页
     * 当不存在是,为当前地址
     * @return 引用页
     */
    public String getReferer() {
    	referer = this.getRequest().getHeader("referer");
    	if (StringUtils.isBlank(referer)) {
    		referer = this.getRequest().getRequestURI();
    		String queryString = this.getRequest().getQueryString();
    		if (StringUtils.isNotBlank(queryString)) {
    			referer += "?" + queryString;
    		}
    	}
    	//referer = this.getResponse().encodeURL(referer);
		//this.referer = HtmlUtils.urlEncode(backurl, "utf-8");
    	return referer;
    }
    
    /**
     * 返回回退地址
     * @return String
     */
    public String getBackurl() {
		if(StringUtils.isBlank(backurl)) {
			backurl = getReferer();
		}
		try {
			backurl = URLDecoder.decode(backurl, "UTF-8");
		} catch (UnsupportedEncodingException e) {
		}
		return backurl;
	}

	public void setBackurl(String backurl) {
		//处理回退地址,回退地址中,去掉退回地址中的回退地址
		if(StringUtils.isNotBlank(backurl)) {
			int i = 0;
			if ((i = backurl.indexOf("?")) > 0) {
				//存在请求参数,判断“backurl”位置
				String params = backurl.substring(i + 1);
				int j =0;
				if ((j = params.indexOf("backurl")) > 0) {
					params =  params.substring(0, j);
				}
			}
		}
		try {
			backurl = URLEncoder.encode(backurl, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		this.backurl = this.getResponse().encodeURL(backurl);
		//this.backurl = HtmlUtils.urlEncode(backurl, "utf-8");
	}

	/**
	 * 返回当前地址(包含参数)
	 * @return String
	 */
	public String getCurrentUrl() {
		String url = this.getRequest().getRequestURI();
		String queryString = this.getRequest().getQueryString();
		if (StringUtils.isNotBlank(queryString)) {
			url += "?" + queryString;
		}
		return this.getResponse().encodeURL(url);
	}
	/**
     * 返回多站点application的当前站点
     * 
     * @return 站点模型
     */
    public Site getSite() {
    	Site site = MultisiteContextHolder.getSite();
    	if (site == null) {
    		//#application.multisite.nof.found=The WebSite not found from this application.
    		log.warn(getText("application.multisite.nof.found"));
    	}
    	return site;
    }
    
    /**
     * 返回当前Locale.
     * 不存在时返回默认Locale.getDefault();
     * 
     * @return locale
     */
    public Locale getLocale() {
    	return LocaleContextHolder.getLocale();
    }
    
    //~ protected method ===================================================================
    /**
     * 受保护方法,返回HttpServletRequest
     *
     * @return 当前的 request
     */
    protected HttpServletRequest getRequest() {
        return ServletActionContext.getRequest();
    }

    /**
     * 受保护方法,返回HttpServletResponse
     *
     * @return 当前的 responseorg.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse
     */
    protected HttpServletResponse getResponse() {
        return ServletActionContext.getResponse();
    }
    
    /**
     * 受保护方法,返回ServletContext
     *
     * @return 当前的 ServletContext
     */
    protected ServletContext getServletContext() {
    	return ServletActionContext.getServletContext();
    }

    /**
     * 受保护方法,返回HttpSession. 如果不存在,创建它.
     *
     * @return 从request取得session (request.getSession()).
     */
    protected HttpSession getSession() {
        return getRequest().getSession();
    }
    
    /**
     * 受保护方法,在当前线程中,取得ValueStack并返回.
     * @return ValueStack 值栈
     */
    protected ValueStack getStack() {
    	return ServletActionContext.getContext().getValueStack();
    }
    
    /**
     * 从ServletContext上下文中查找并返回Configuration HashMap.
     *
     * @return application的配置configuration
     */
    @SuppressWarnings("unchecked")
	protected Map<String, Object> getConfiguration() {
        @SuppressWarnings("rawtypes")
        // 得到系统中所有站点配置
		Map<String, Object> config = (Map) getSession().getServletContext().getAttribute(Constants.CONFIG);
        // 不存在配置,返回空Map
        if (config == null) {
        	//#application.config.not.found=The configuration '{0}' not found from current application.
        	log.warn(getText("application.config.not.found", Constants.CONFIG));
            return new HashMap<String, Object>();
        }
        // 使用当前站点配置
        Site site = getSite();
        String key = site.getDomain();
        return (Map<String, Object>) config.get(key);
    }

	public boolean isAjax() {
		return ajax;
	}

	public void setAjax(boolean ajax) {
		this.ajax = ajax;
	}

	public boolean isPiece() {
		return piece;
	}

	public void setPiece(boolean piece) {
		this.piece = piece;
	}
	
	/**
	 * 取消该方法@see {@link com.opensymphony.xwork2.ActionSupport#getErrors()}
	 * 防止与FieldErrors并存时，出现两次错误提示。
	 * @version 2.3.4.1
	 */
	@Override
	public Map<String, List<String>> getErrors() {
		// ~ 取消错误消息
		return null;
	}
	
	// ~ 客户端验证资源部分 ========================================================
	// Bean工厂
	public ObjectFactory getObjectFactory() {
		return this.objectFactory;
	}
	
	@Inject("clientValidation")
    public void setValidationResolver(ValidationResolver validationResolver) {
		this.validationResolver = validationResolver;
	}

	@Inject
    public void setObjectFactory(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }
    
    @Inject
    public void setActionValidatorManager(ActionValidatorManager mgr) {
        this.actionValidatorManager = mgr;
    }
	
    @Inject
    public void setActionMapper(ActionMapper mapper) {
        this.actionMapper = mapper;
    }
    
    //@Inject
    //public void setUrlHelper(UrlHelper urlHelper) {
    //    this.urlHelper = urlHelper;
    //}
    
    @Inject
    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }
    
	@SuppressWarnings("rawtypes")
	@Override
	public String getValidateMetadata(String action, String field) {
		String actionName;
		action = this.findString(action);

		ActionMapping nameMapping = actionMapper.getMappingFromActionName(action);
        actionName = nameMapping.getName();
        //String actionMethod = nameMapping.getMethod();
        
		List<Validator> vList = actionValidatorManager.getValidators(getClass(), actionName);
		if (vList != null) vList.toArray();
		//List<Validator> all = actionValidatorManager.getValidators(actionClass, (String) getParameters().get("actionName"));
		
		return "{require:true}";
	}
	
	@Override
	public String getValidateMetadata(String namespace, String action, String field) {
		
		String metadata = "";
		String actionName;
		actionName = action;
		if (this.currentValidators == null) {
			evaluateValidateMetadata(namespace, actionName);
		}
		if (currentValidators != null) {
			metadata = validationResolver.resolve(field, currentValidators);
		}
		return metadata;
	}
	
	private void evaluateValidateMetadata(String namespace, String actionName) {
		
		Class<?> actionClass = getClass();
        RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration();
        ActionConfig actionConfig = runtimeConfiguration.getActionConfig(namespace, actionName);
        try {
        	actionClass = getObjectFactory().getClassInstance(actionConfig.getClassName());
		} catch (ClassNotFoundException e) {
			// not found this class, LOG?
		}
        this.currentValidators = actionValidatorManager.getValidators(actionClass, actionName);
	}

	@Override
	public Object getValidateRule() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Validator<?>> evaluateValidator(String url) {
		// TODO Auto-generated method stub
		return null;
	}

	public String findString(String expr) {
    	 return (String) findValue(expr, String.class);
    }
    
    public Object findValue(String expr, Class<?> clazz) {
    	 if (clazz == String.class) {
         	return TextParseUtil.translateVariables('%', expr, getStack());
         } else {
             expr = stripExpressionIfAltSyntax(expr);

             return getStack().findValue(expr, clazz, throwExceptionOnELFailure);
         }
    }
    
    /**
     * 如果支持altsyntax (%{...})语法表达式,则根据值栈取值;
     * 如果不支持,则返回原值、 
     * @param expr 表达式,非空
     * @return 表达式内容
     */
	protected String stripExpressionIfAltSyntax(String expr) {
		return stripExpressionIfAltSyntax(getStack(), expr);
	}
	
    /**
     * 使用值栈表达式取得相应内容,如果不支持表达式,则不适应值栈取值
     * @param stack 当前上下文的ValueStack 
     * @param expr 表达式,非空
     * @return 表达式内容
     * @see <code>struts.properties</code> 配置struts.tag.altSyntax=true 则允许,默认开启
     */
	public static String stripExpressionIfAltSyntax(ValueStack stack, String expr) {
		if (altSyntax(stack)) {
            // does the expression start with %{ and end with }? if so, just cut it off!
            if (expr.startsWith("%{") && expr.endsWith("}")) {
                return expr.substring(2, expr.length() - 1);
            }
        }
		return expr;
	}

    /**
     * 是否支持altsyntax (%{...})语法表达式
     * <p/>
     * @param stack ValueStack 
     * @return boolean
     * See <code>struts.properties</code> where the altSyntax flag is defined.
     * exp:struts.tag.altSyntax=true or false
     */
	public static boolean altSyntax(ValueStack stack)  {
        return ContextUtil.isUseAltSyntax(stack.getContext());
	}
}
